#include <assert.h>
#include <errno.h>
#include "ConditionVariable.h"

namespace why
{
    namespace synchronization
    {

#if defined (OS_WIN)

        ConditionVariable::ConditionVariable(void)
        {
            InitializeConditionVariable(&condition_);
            InitializeCriticalSection(&user_mutex_);
        }

        ConditionVariable::~ConditionVariable(void)
        {
            DeleteCriticalSection(&user_mutex_);
        }

        void ConditionVariable::Wait()
        {
            verify(TimedWait(INFINITE));
        }

        bool ConditionVariable::TimedWait(uint32 wait_milliseconds)
        {
            DWORD timeout = static_cast<DWORD>(wait_milliseconds);
            EnterCriticalSection(&user_mutex_);
            BOOL result = SleepConditionVariableCS(&condition_, &user_mutex_, timeout);
            if (FALSE == result)
            {
                assert(WAIT_TIMEOUT == GetLastError());
            }
            LeaveCriticalSection(&user_mutex_);
            return (result == TRUE);
        }

        void ConditionVariable::Signal()
        {
            WakeConditionVariable(&condition_);
        }

        void ConditionVariable::Broadcast()
        {
            WakeAllConditionVariable(&condition_);
        }

#else 

        ConditionVariable::ConditionVariable(void)
        {
        	verify(0 == pthread_mutex_init(&user_mutex_, NULL));
        	verify(0 == pthread_cond_init(&condition_, NULL));   // Use the real-time clock type
        }

        ConditionVariable::~ConditionVariable(void)
        {
        	verify(0 == pthread_mutex_destroy(&user_mutex_));
        	verify(0 == pthread_cond_destroy(&condition_));
        }

        void ConditionVariable::Wait()
        {
        	verify(0 == pthread_mutex_lock(&user_mutex_));
        	verify(0 == pthread_cond_wait(&condition_, &user_mutex_));
        	verify(0 == pthread_mutex_unlock(&user_mutex_));
        }

        bool ConditionVariable::TimedWait(uint32 wait_milliseconds)
        {
        	// It may has some wrong, if the system time is changed.
        	struct timespec ts;
        	ts.tv_sec = wait_milliseconds / 1000;
        	ts.tv_nsec = (wait_milliseconds % 1000) * 1000000;

        	verify(0 == pthread_mutex_lock(&user_mutex_));
        	int cond_timedwait_result = pthread_cond_timedwait(&condition_, &user_mutex_, &ts);
        	assert(0 == cond_timedwait_result || ETIMEDOUT == cond_timedwait_result);
        	verify(0 == pthread_mutex_unlock(&user_mutex_));
        	return (0 == cond_timedwait_result);
        }

        void ConditionVariable::Signal()
        {
        	verify(0 == pthread_cond_signal(&condition_));
        }

        void ConditionVariable::Broadcast()
        {
        	verify(0 == pthread_cond_broadcast(&condition_));
        }

#endif 
    }
}
